package com.kantboot.system.user.balance.service.impl;

import com.kantboot.system.user.balance.dao.repository.SysBalanceRepository;
import com.kantboot.system.user.balance.dao.repository.SysUserBalanceChangeRecordRepository;
import com.kantboot.system.user.balance.dao.repository.SysUserBalanceRepository;
import com.kantboot.system.user.balance.dao.repository.SysUserBalanceShowRepository;
import com.kantboot.system.user.balance.domain.dto.ChangeBalanceDTO;
import com.kantboot.system.user.balance.domain.entity.SysBalance;
import com.kantboot.system.user.balance.domain.entity.SysUserBalance;
import com.kantboot.system.user.balance.domain.entity.SysUserBalanceChangeRecord;
import com.kantboot.system.user.balance.domain.entity.SysUserBalanceShow;
import com.kantboot.system.user.balance.exception.HasBalanceChangeRecordSubtractException;
import com.kantboot.system.user.balance.exception.InsufficientBalanceException;
import com.kantboot.system.user.balance.service.ISysBalanceService;
import com.kantboot.util.common.exception.BaseException;
import com.kantboot.util.core.redis.RedisUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 用户余额服务实现
 */
@Slf4j
@Service
public class SysBalanceServiceImpl implements ISysBalanceService {

    @Resource
    private SysBalanceRepository balanceRepository;

    @Resource
    private SysUserBalanceChangeRecordRepository userBalanceChangeRecordRepository;

    @Resource
    private SysUserBalanceRepository userBalanceRepository;

    @Resource
    private SysUserBalanceShowRepository userBalanceShowRepository;

    @Resource
    private RedisUtil redisUtil;


    @Override
    public void changeBalance(ChangeBalanceDTO changeBalanceDTO) {
        // 是否存在减少余额的记录没有处理
        String redisKey = "SysBalance:hasBalanceChangeRecordSubtract:" + changeBalanceDTO.getUserId();
        if (changeBalanceDTO.getNumber().doubleValue() < 0 &&
                Boolean.TRUE.equals(redisUtil.hasKey(redisKey))) {
            // 有未处理完的余额减少变动记录
            throw new HasBalanceChangeRecordSubtractException();
        }

        // 如果余额变动为负数
        if (changeBalanceDTO.getNumber().doubleValue() < 0) {
            // 加锁
            redisUtil.set(redisKey, "1");
        }

        // 获取用户余额数量
        BigDecimal userBalanceNumber = getUserBalanceNumber(changeBalanceDTO.getUserId(), changeBalanceDTO.getBalanceCode());
        // 如果余额不足
        if (changeBalanceDTO.getNumber().doubleValue() < 0 &&
                userBalanceNumber.doubleValue() < Math.abs(changeBalanceDTO.getNumber().doubleValue())) {
            // 解锁
            redisUtil.delete(redisKey);
            // 抛出余额不足的异常
            throw new InsufficientBalanceException();
        }

        SysUserBalanceChangeRecord record = new SysUserBalanceChangeRecord();
        // 设置用户ID
        record.setUserId(changeBalanceDTO.getUserId());
        // 设置变动金额
        record.setNumber(changeBalanceDTO.getNumber());
        // 设置余额编码
        record.setBalanceCode(changeBalanceDTO.getBalanceCode());
        // 设置变动事由编码
        record.setReasonCode(changeBalanceDTO.getReasonCode());
        // 设置状态码
        record.setStatusCode(SysUserBalanceChangeRecord.STATUS_CODE_NOT_PROCESSED);
        // 保存记录
        userBalanceChangeRecordRepository.save(record);

    }


    /**
     * 保存进展示表
     */
    private void saveShowTable(Long userId, String balanceCode) {
        SysUserBalance userBalance = getUserBalance(userId, balanceCode);
        SysUserBalanceShow userBalanceShow = userBalanceShowRepository.findByUserIdAndBalanceCode(userId, balanceCode);
        if (userBalanceShow == null) {
            userBalanceShow = new SysUserBalanceShow();
            userBalanceShow.setUserId(userId);
            userBalanceShow.setBalanceCode(balanceCode);
            userBalanceShow.setNumber(userBalance.getNumber());
            userBalanceShowRepository.save(userBalanceShow);
            return;
        }
        userBalanceShow.setNumber(userBalance.getNumber());
        userBalanceShowRepository.save(userBalanceShow);
    }

    @Override
    public void handleBalanceChangeRecord(Long userBalanceRecordId) {
        Boolean lock = redisUtil.lock("handleBalanceChange:" + userBalanceRecordId, 1, TimeUnit.MINUTES);
        if (lock) {
            throw BaseException.of("handling", "正在处理中");
        }

        // 查询余额变动记录
        SysUserBalanceChangeRecord record = userBalanceChangeRecordRepository.findById(userBalanceRecordId).orElse(null);
        if (record == null) {
            return;
        }
        // 如果不是未处理状态，则不处理
        if (!SysUserBalanceChangeRecord.STATUS_CODE_NOT_PROCESSED.equals(record.getStatusCode())) {
            return;
        }
        // 获取用户余额, 因为此方法中如果不存在则会初始化
        SysUserBalance userBalance = getUserBalance(record.getUserId(), record.getBalanceCode());
        // 更新余额
        userBalanceRepository.changeBalance(record.getUserId(), record.getBalanceCode(), record.getNumber());
        // 如果余额小于0
        if (record.getNumber().doubleValue() < 0) {
            // 是否存在减少余额的记录没有处理
            String redisKey = "SysBalance:hasBalanceChangeRecordSubtract:" + userBalance.getUserId();
            // 解锁
            redisUtil.delete(redisKey);
            log.info("删除余额减少记录锁");
        }

        // 更新状态码
        record.setStatusCode(SysUserBalanceChangeRecord.STATUS_CODE_SUCCESS);
        // 保存记录
        userBalanceChangeRecordRepository.save(record);
        SysBalance byCode = balanceRepository.findByCode(record.getBalanceCode());
        if (byCode != null && byCode.getIsVisible() != null && byCode.getIsVisible()) {
            // 保存进展示表
            saveShowTable(record.getUserId(), record.getBalanceCode());
        }
        // 解锁
        redisUtil.unlock("handleBalanceChange:" + userBalanceRecordId);
    }

    @Override
    public List<SysUserBalanceChangeRecord> getUserBalanceChangeRecordByNotProcessed() {
        return userBalanceChangeRecordRepository.findByStatusCode(SysUserBalanceChangeRecord.STATUS_CODE_NOT_PROCESSED);
    }

    @Override
    public SysUserBalance getUserBalance(Long userId, String balanceCode) {
        SysUserBalance byUserIdAndBalanceCode = userBalanceRepository.findByUserIdAndBalanceCode(userId, balanceCode);
        if (byUserIdAndBalanceCode == null) {
            // 初始化
            SysUserBalance userBalance = new SysUserBalance();
            userBalance.setBalanceCode(balanceCode);
            userBalance.setNumber(BigDecimal.ZERO);
            userBalance.setUserId(userId);
            userBalanceRepository.save(userBalance);
            return userBalance;
        }
        return byUserIdAndBalanceCode;
    }

    @Override
    public BigDecimal getUserBalanceNumber(Long userId, String balanceCode) {
        SysUserBalance userBalance = getUserBalance(userId, balanceCode);
        if (userBalance == null) {
            return BigDecimal.ZERO;
        }
        if (userBalance.getNumber() == null) {
            return BigDecimal.ZERO;
        }
        return userBalance.getNumber();
    }
}
